#include <vector>
#include <cmath>
#include <cstdlib>
#include <limits>
#include "tgaimage.h"
#include "model.h"
#include "geometry.h"

const TGAColor white = TGAColor(255, 255, 255, 255);
const TGAColor red   = TGAColor(255, 0,   0,   255);
Model *model = NULL;
const int width  = 800;
const int height = 800;

void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) {
    bool steep = false;
    if (std::abs(x0-x1)<std::abs(y0-y1)) {
        std::swap(x0, y0);
        std::swap(x1, y1);
        steep = true;
    }
    if (x0>x1) {
        std::swap(x0, x1);
        std::swap(y0, y1);
    }

    for (int x=x0; x<=x1; x++) {
        float t = (x-x0)/(float)(x1-x0);
        int y = y0*(1.-t) + y1*t;
        if (steep) {
            image.set(y, x, color);
        } else {
            image.set(x, y, color);
        }
    }
}

Vec3f barycentric(Vec3f* pts, Vec3f P) {
     int Xa = pts[0].x;
    int Xb = pts[1].x;
    int Xc = pts[2].x;
    int Ya = pts[0].y;
    int Yb = pts[1].y;
    int Yc = pts[2].y;
    float u1 = (float)Xa * Yb - Xb * Ya;
    float u = ((Ya - Yb) * P.x + (Xb - Xa) * P.y + u1) / ((Ya - Yb) * Xc + (Xb - Xa) * Yc + u1);
    float v1 = (float)Xa * Yc - Xc * Ya;
    float v = ((Ya - Yc) * P.x + (Xc - Xa) * P.y + v1) / ((Ya - Yc) * Xb + (Xc - Xa) * Yb + v1);
    float a = 1 - u - v;
    //return Vec3f(1 - u - v, u, v);

    //The order matters！！！！！
    return Vec3f(1 - u - v, v, u);
}

void triangle(Vec3f *pts, float *zbuffer, TGAImage &image, Vec2i*uvs,float intensity,TGAImage&texture) {
    
    float minx = std::min(pts[0].x, std::min(pts[1].x, pts[2].x));
    float maxx = std::max(pts[0].x, std::max(pts[1].x, pts[2].x ));
    float miny = std::min(pts[0].y, std::min(pts[1].y, pts[2].y ));
    float maxy = std::max(pts[0].y, std::max(pts[1].y, pts[2].y ));
    int min_x = (int)std::floor(minx);
    int max_x = (int)std::ceil(maxx);
    int min_y = (int)std::floor(miny);
    int max_y = (int)std::ceil(maxy);

    for (int i = min_x; i <= max_x; i++)
    {
        for (int j = min_y; j <= max_y; j++)
        {
            Vec3f P(i, j, 0);
            Vec2i uvP;
            Vec3f baryCoord = barycentric(pts, P);
            if (baryCoord.x < -0.01 || baryCoord.y < -0.01 || baryCoord.z < -0.01)
                continue;
            float z_interpolation = baryCoord.x * pts[0].z + baryCoord.y * pts[1].z + baryCoord.z * pts[2].z;
            uvP = uvs[0] * baryCoord.x + uvs[1] * baryCoord.y + uvs[2] * baryCoord.z;
            if (z_interpolation > zbuffer[static_cast<int>(P.x + P.y * width)])
            {
                zbuffer[static_cast<int>(i + j * width)] = z_interpolation;
                TGAColor color = texture.get(uvP.x,uvP.y);
                image.set(P.x, P.y, TGAColor(color.bgra[2] * intensity, color.bgra[1] * intensity, color.bgra[0] * intensity, 255));
            }
        }
    }
}

Vec3f world2screen(Vec3f v) {
    return Vec3f(int((v.x+1.)*width/2.+.5), int((v.y+1.)*height/2.+.5), v.z);
}

int main(int argc, char** argv) {
    if (2==argc) {
        model = new Model(argv[1]);
    } else {
        model = new Model("obj/african_head/african_head.obj");
    }
    Vec3f light_dir(0, 0, -1);
    TGAImage texture;
    texture.read_tga_file("obj/african_head/african_head_diffuse.tga");
    texture.flip_vertically();
    
    float *zbuffer = new float[width*height];
    for (int i=width*height; i--; zbuffer[i] = -std::numeric_limits<float>::max());

    TGAImage image(width, height, TGAImage::RGB);
    for (int i=0; i<model->nfaces(); i++) {
        std::vector<int> face = model->face(i);
        Vec3f screenCoords[3];//存贮第i个面片三个顶点的屏幕坐标
        Vec3f worldCoords[3];//存储第I个面片三个顶点的世界坐标
        for (int j = 0; j < 3; j++)
        {
            worldCoords[j] = model->vert(face[j]);
            screenCoords[j] = world2screen(worldCoords[j]);//转换为屏幕坐标
        }

        Vec3f normal = cross((worldCoords[2] - worldCoords[0]),(worldCoords[1] - worldCoords[0]));
        normal.normalize();
        float intensity = normal * light_dir;
        if (intensity > 0)
        {
            Vec2i uv[3];
            for (int j = 0; j < 3; j++){
                uv[j].x = model->uv(i, j).x*texture.get_width();          
                uv[j].y = model->uv(i, j).y*texture.get_height();
            } 
            triangle(screenCoords, zbuffer, image,uv, intensity,texture);
        }    
    }

    image.flip_vertically(); // i want to have the origin at the left bottom corner of the image
    image.write_tga_file("output.tga");
    delete model;
    return 0;
}

